// 一、将下面异步代码使用 Promise 的方法改进
// setTimeout(function () {
//   var a = 'hello'

//   setTimeout(function () {
//     var b = 'lagou'

//     setTimeout(function () {
//       var c = 'I love U'
//       console.log(a + b + c)
//     }, 10)
//   }, 10)
// }, 10)

var a = 'hello';
var b = 'lagou';
var c = 'I love U';
Promise.all([a, b, c]).then(console.log(a + b + c));

// 二、基于以下代码完成四个练习
const fp = require('lodash/fp');
// 数据
// horsepower 马力, dollar_value 价格, in_stock 库存
const cars = [
  {
    name: 'Ferrari FF',
    horsepower: 660,
    dollar_value: 700000,
    in_stock: true,
  },
  {
    name: 'Spyker C12 Zagato',
    horsepower: 650,
    dollar_value: 648000,
    in_stock: false,
  },
  {
    name: 'Jaguar XKR-S',
    horsepower: 550,
    dollar_value: 132000,
    in_stock: false,
  },
  {
    name: 'Aston Martin One-77',
    horsepower: 750,
    dollar_value: 1850000,
    in_stock: true,
  },
  {
    name: 'Pagani Huayra',
    horsepower: 700,
    dollar_value: 13500000,
    in_stock: false,
  },
];

// 练习1：使用函数组合 fp.flowRight() 重新实现下面这个函数
// let isLastInStock = function (cars) {
//   // 获取最后一条数据
//   let last_car = fp.last(cars);
//   // 获取最后一条数据的 in_stock 属性值
//   return fp.prop('in_stock', last_car);
// };

let isLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last);
console.log('isLastInStock', isLastInStock(cars));

// 练习2：使用 fp.flowRight()、fp.prop() 和 fp.first() 获取第一个 car 的 name
let firstCarName = fp.flowRight(fp.prop('name'), fp.first);
console.log('firstCarName', firstCarName(cars));

// 练习3：使用帮助函数 _average 重构 averageDollarValue，使用函数组合方式实现
let _average = function (xs) {
  return fp.reduce(fp.add, 0, xs) / xs.length;
}; // 无须改动

// let averageDollarValue = function (res) {
//   let dollar_value = fp.map(function (car) {
//     return car.dollar_value
//   }, cars)

//   return _average(dollar_value)
// }

let averageDollarValue = fp.flowRight(
  _average,
  fp.map(car => car.dollar_value)
);
console.log('averageDollarValue', averageDollarValue(cars));

// 练习4：使用 flowRight() 写一个 sanitizeName() 函数
// 返回一个下划线连接的小写字符串，把数组中的 name 转换为这种形式：例如：sanitizeNames([“Hello World”]) => [“hello_world”]

let _underscore = fp.replace(/\W+/g, '_'); // 无须改动，并在 sanitizeNames 中使用它

const sanitizeNames = fp.map(fp.flowRight(fp.toLower, _underscore, fp.prop('name')));
console.log('sanitizeNames', sanitizeNames(cars));

// 三、基于下面提供的代码，完成后续的四个练习
// support.js

// 练习1：使用 fp.add(x, y) 和 fp.map(f, x) 创建一个能让 functor 里的值增加的函数 ex1
// app.js
const { Maybe, Container } = require('./support');
const { reject } = require('lodash');
let maybe = Maybe.of([5, 6, 1]);
// console.log(
//   'maybe',
//   maybe.map(x => console.log(x))
// );
// let ex1 = () => {
//   // 要实现的函数
// }

// let ex1 = fp.map(x => console.log(x));
let ex1 = maybe.map(fp.map(x => fp.add(x, 1)));
console.log('ex1', ex1);

// 练习2：实现一个函数 ex2，能够使用 fp.first 获取列表的第一个元素
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do']);
let ex2 = fp.map(fp.first);
console.log(ex2(xs));

// 练习3：实现一个函数 ex3，使用 safeProp 和 fp.first 找到 user 的名字的首字母
let safeProp = fp.curry(function (x, o) {
  return Maybe.of(o[x]);
});
let user = { id: 2, name: 'Albert' };
let ex3 = fp.flowRight(fp.map(fp.first), safeProp('name'));
console.log('ex3', ex3(user));

// 练习4：使用 Maybe 重写 ex4，不要有 if 语句
// let ex4 = function (n) {
//   if (n) { return parseInt(n) }
// }

let ex4 = fp.flowRight(Maybe.map(parseInt), Maybe.of);

// 四、手写 Promise 源码
// Promise 有三种状态，分别是 待定（pending）、成功（fulfilled）和失败（reject）
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECT = 'reject';

class MyPromise {
  constructor(executor) {
    // 使用 try catch 捕捉错误
    try {
      // Promise 构造函数执行时马上调用函数 executor
      executor(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }

  // 初始化状态
  status = PENDING;
  value = undefined;
  error = undefined;
  successCallBack = [];
  failCallBack = [];

  resolve = value => {
    // 因为 Promise 能保持的状态只有一种且不能更改，所以如果当前状态不是 'pending' 就停止执行
    if (this.status !== PENDING) return;
    // 调用 resolve 函数时将状态改为 'fulfilled'
    this.status = FULFILLED;
    // 储存成功的值
    this.value = value;
    // 执行成功回调函数
    // this.successCallBack && this.successCallBack(this.value)
    // 从成功回调函数数组中移除第一项
    while (this.successCallBack.length) this.successCallBack.shift()(this.value);
  };

  reject = error => {
    if (this.status !== PENDING) return;
    // 调用 resolve 函数时将状态改为 'reject'
    this.status = REJECT;
    // 储存错误原因
    this.error = error;
    // 执行失败回调函数
    // this.failCallBack && this.failCallBack(this.error)
    // 从失败回调函数数组中移除第一项
    while (this.failCallBack.length) this.failCallBack.shift()(this.value);
  };

  // then 方法内部判断当前的状态
  then(successCallBack, failCallBack) {
    // 判断 then 方法中是否有参数传递过来
    successCallBack = successCallBack ? successCallBack : value => value;
    failCallBack = failCallBack
      ? failCallBack
      : error => {
          throw error;
        };

    let promise = new MyPromise((resolve, reject) => {
      if (this.status === FULFILLED) {
        // 这是同样使用 try catch 捕捉错误
        // 为了能正确地获取到 promise2，所以要使用 setTimeout 转换成异步代码
        setTimeout(() => {
          try {
            // 如当前状态为 FULFILLED，执行成功回调函数
            // successCallBack(this.value);
            let value = this.successCallBack(this.value);
            // resolve(value);
            // 判断 value 的值是普通值还是 promise 对象
            resolvePromise(promise, value, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECT) {
        // 如当前状态为 FULFILLED，执行失败回调函数
        // failCallBack(this.error);
        setTimeout(() => {
          // 捕捉处理失败回调函数时的错误
          try {
            let value = failCallBack(this.error);
            resolvePromise(promise, value, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 储存回调函数
        // 在 push 函数的时候处理错误
        this.successCallBack.push(() => {
          setTimeout(() => {
            try {
              let value = successCallBack(this.value);
              resolvePromise(promise, value, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.failCallBack.push(() => {
          setTimeout(() => {
            try {
              let value = failCallBack(this.error);
              resolvePromise(promise, value, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise;
  }

  catch(failCallBack) {
    return this.then(undefined, failCallBack);
  }

  static all(array) {
    let result = [];
    let count = 0;

    return new MyPromise((resolve, reject) => {
      function countData(index, value) {
        result[index] = value;
        count++;
        // 判断数组内所有项是否执行完毕
        if (count === array.length) {
          // 如果数组内所有内容执行完，处理 result
          resolve(result);
        }
      }

      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        // 判断当前对象是否为自身
        if (current instanceof array[i]) {
          current.then(
            value => countData(i, value),
            error => reject(error)
          );
        } else {
          // 如果为普通值
          countData(i, array[i]);
        }
      }
    });
  }

  static resolve(value) {
    // 判断 value 是否为 promise 对象
    if (value instanceof MyPromise) return value;

    return new MyPromise(resolve => resolve(value));
  }

  static finally(callback) {
    // 调用 then 方法来判断状态
    return this.then(
      // 等待 callback 执行完成，借助 resolve 和 reject 方法
      value => {
        return MyPromise.resolve(callback().then(() => value));
      },
      error => {
        return MyPromise.reject(
          callback().then(() => {
            throw error;
          })
        );
      }
    );
  }
}

function resolvePromise(promise, value, resolve, reject) {
  // 判断 value 是否为自身
  if (value === promise) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }

  // 判断 value 是否为 Promise 对象
  if (value instanceof MyPromise) {
    // 如果是的话，调用 then 方法查看状态
    value.then(resolve, reject);
  } else {
    // 如果是普通值的话，直接调用 resolve
    resolve(value);
  }
}

module.exports = MyPromise;
